#include "ComponentAutoJump.h"
#include "Components/ComponentCreature.h"
#include "Components/ComponentLocomotion.h"
#include "Components/ComponentBody.h"
#include "Subsystems/SubsystemTerrain.h"
#include "Subsystems/SubsystemTimeOfDay.h"

#include "Managers/BlocksManager.h"

#include "GameSingleton.h"
#include "GameRandom.hpp"
#include "GameRegistry.hpp"
#include "GameThreadPool.hpp"

#include "Engine/Vector3.hpp"
#include "nlohmann/json.hpp"

namespace PocketSurvival
{
    void ComponentAutoJump::update(float dt)
    {
        SubsystemTerrain *terrain = GameSingleton::singletonPtr->getSingleton<SubsystemTerrain>();
        SubsystemTimeOfDay *timeOfDay = GameSingleton::singletonPtr->getSingleton<SubsystemTimeOfDay>();

        ComponentBody *body = componentCreature->componentBody;

        if (alwayEnabled && timeOfDay->getGameTime() - lastAutoJumpTime > 0.25)
        {
            std::shared_ptr<Vector2> lastWalkOrder = componentCreature->componentLocomotion->lastWalkOrder;
            if (lastWalkOrder != nullptr)
            {
                Vector2 vector(body->collisionVelocityChange.posX, body->collisionVelocityChange.posZ);
                if (vector != FastVector2::Zero && collideWithBody == false)
                {
                    Vector2 v = Vector2::Normalize(vector);
                    Vector3 vector2 = body->getMatrix().getRight() * lastWalkOrder->posX + body->getMatrix().getForward() * lastWalkOrder->posY;
                    Vector2 v2 = Vector2::Normalize(Vector2(vector2.posX, vector2.posZ));
                    bool flag = false;
                    Vector3 v3 = FastVector3::Zero;
                    Vector3 vector3 = FastVector3::Zero;
                    Vector3 vector4 = FastVector3::Zero;
                    if (Vector2::Dot(v2, -v) > 0.6f)
                    {
                        Vector3 position;
                        body->getPosition(position);

                        if (Vector2::Dot(v2, FastVector2::UnitX) > 0.6f)
                        {
                            v3 = position + FastVector3::UnitX;
                            vector3 = v3 - FastVector3::UnitZ;
                            vector4 = v3 + FastVector3::UnitZ;
                            flag = true;
                        }
                        else if (Vector2::Dot(v2, -FastVector2::UnitX) > 0.6f)
                        {
                            v3 = position - FastVector3::UnitX;
                            vector3 = v3 - FastVector3::UnitZ;
                            vector4 = v3 + FastVector3::UnitZ;
                            flag = true;
                        }
                        else if (Vector2::Dot(v2, FastVector2::UnitY) > 0.6f)
                        {
                            v3 = position + FastVector3::UnitZ;
                            vector3 = v3 - FastVector3::UnitX;
                            vector4 = v3 + FastVector3::UnitX;
                            flag = true;
                        }
                        else if (Vector2::Dot(v2, -FastVector2::UnitY) > 0.6f)
                        {
                            v3 = position - FastVector3::UnitZ;
                            vector3 = v3 - FastVector3::UnitX;
                            vector4 = v3 + FastVector3::UnitX;
                            flag = true;
                        }
                    }
                    if (flag)
                    {
                        int32_t cellContents = terrain->getCellContents(Terrain::ToCell(v3.posX), Terrain::ToCell(v3.posY), Terrain::ToCell(v3.posZ));
                        int32_t cellContents2 = terrain->getCellContents(Terrain::ToCell(vector3.posX), Terrain::ToCell(vector3.posY), Terrain::ToCell(vector3.posZ));
                        int32_t cellContents3 = terrain->getCellContents(Terrain::ToCell(vector4.posX), Terrain::ToCell(vector4.posY), Terrain::ToCell(vector4.posZ));
                        int32_t cellContents4 = terrain->getCellContents(Terrain::ToCell(v3.posX), Terrain::ToCell(v3.posY) + 1, Terrain::ToCell(v3.posZ));
                        int32_t cellContents5 = terrain->getCellContents(Terrain::ToCell(vector3.posX), Terrain::ToCell(vector3.posY) + 1, Terrain::ToCell(vector3.posZ));
                        int32_t cellContents6 = terrain->getCellContents(Terrain::ToCell(vector4.posX), Terrain::ToCell(vector4.posY) + 1, Terrain::ToCell(vector4.posZ));

                        Block *block = GameSingleton::blocksManager->blocks[cellContents];
                        Block *block2 = GameSingleton::blocksManager->blocks[cellContents2];
                        Block *block3 = GameSingleton::blocksManager->blocks[cellContents3];
                        Block *block4 = GameSingleton::blocksManager->blocks[cellContents4];
                        Block *block5 = GameSingleton::blocksManager->blocks[cellContents5];
                        Block *block6 = GameSingleton::blocksManager->blocks[cellContents6];

                        if (block->NoAutoJump == false &&
                            ((block->IsCollidable == true && block4->IsCollidable == false) ||
                             (block2->IsCollidable == true && block5->IsCollidable == false) ||
                             (block3->IsCollidable == true && !block6->IsCollidable == false)))
                        {
                            componentCreature->componentLocomotion->jumpOrder = MathUtils::Max(jumpStrength, componentCreature->componentLocomotion->jumpOrder);
                            lastAutoJumpTime = timeOfDay->getGameTime();
                        }
                    }
                }
            }
        }
        collideWithBody = false;
    }


    static const std::string componentName = "AutoJump";
    const std::string &ComponentAutoJump::getName() const
    {
        return componentName;
    }
    const std::string &ComponentAutoJump::GetName()
    {
        return componentName;
    }

    void ComponentAutoJump::load(const nlohmann::json &jsonData)
    {
        componentCreature = GameSingleton::gameRegistry->get<ComponentCreature *>(entity);

        alwayEnabled = jsonData["AlwayEnabled"].get<bool>();
        jumpStrength = jsonData["JumpStrength"].get<float>();
    }

} // namespace PocketSurvival
